/**
 * 
 */
package br.embedded.ufcg.rmi;

import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import org.jgrapht.Graph;
import org.jgrapht.generate.ScaleFreeGraphGenerator;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.SimpleWeightedGraph;

import br.edu.ufcg.embedded.mysocial.UserGenerator;
import br.edu.ufcg.embedded.mysocial.VertexGenerator;
import br.edu.ufcg.embedded.util.Compressor;
import br.edu.ufcg.embedded.util.Topology;
import br.embedded.socialapp.business.Usuario;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver;

/**
 * @author daniel
 *
 */
public class Server extends UnicastRemoteObject implements Process
{
	private static int MAX_CONNECTIONS;
	private static int NUMBER_OF_VERTICES;
	private static int VERTEX_DEGREE;
	private static Topology TOPOLOGY;
	private static String RMI_REGISTRY;
	
	private Graph<Usuario, DefaultEdge> graph;
	byte[] compressedGraph;
	private Compressor compressor;
	private Hashtable<String, Long> connectionMap;
	private Hashtable<String, List<Usuario>> jobMap;
	
	private List<List<Usuario>> lists;
	
	public Server() throws RemoteException
	{
		super();
		
		connectionMap = new Hashtable<String, Long>(MAX_CONNECTIONS);
		jobMap = new Hashtable<String, List<Usuario>>();
		createGraph();
		createLists();
		compressGraph();
	}
	
	@Override
	public byte[] getGraph() throws RemoteException {
		
		return compressedGraph;
	}
	
	@Override
	public byte[] getJobList(String ip) throws RemoteException {
		
		XStream xstream = new XStream(new JettisonMappedXmlDriver());
		compressor = new Compressor();
		byte[] compressedJob = compressedGraph = compressor.compress(xstream.toXML(jobMap.get(ip)));
		
		return compressedJob;
	}
	
	@Override
	public void register(String client) throws RemoteException {
		
		if (!connectionMap.containsKey(client) && connectionMap.size() < MAX_CONNECTIONS)
		{
			connectionMap.put(client, Long.MAX_VALUE);
			jobMap.put(client, getAvailableList());
			
			System.out.println("Server: a new client: " + client + " was registered ");
		}
		else
			System.out.println("Server: the client: " + client + " is already registered.");
	}
	
	@Override
	public void setEndTime(String client, long time) throws RemoteException {
		if (connectionMap.containsKey(client)) {
			connectionMap.put(client, time);
			System.out.println("Server: the client " + client + " has finished. Elapsed time: " + time);
		}
	}
	
	private void createGraph()
	{
		if (TOPOLOGY == Topology.ScaleFree)
		{
			graph = new SimpleWeightedGraph<Usuario, DefaultEdge>(DefaultEdge.class);
			ScaleFreeGraphGenerator<Usuario, DefaultEdge> generator = new ScaleFreeGraphGenerator<Usuario, DefaultEdge>(100);
			generator.generateGraph(graph, new VertexGenerator(), null);
		}
		else {
			
			List<Usuario> usersList = UserGenerator.generateUsers(NUMBER_OF_VERTICES);
			graph = UserGenerator.buildFriendshipGraph(usersList, VERTEX_DEGREE);
		}
		
		System.out.println("Server: Graph was generated with " + graph.vertexSet().size() + " nodes.");
	}
	
	private void compressGraph()
	{
		XStream xstream = new XStream(new JettisonMappedXmlDriver());
		compressor = new Compressor();
		compressedGraph = compressor.compress(xstream.toXML(graph));
		
		System.out.println("Server: the graph is ready to be consumed.");
	}
	
	private synchronized void createLists()
	{
		List<Usuario> list = new ArrayList<Usuario>(graph.vertexSet());
		this.lists = new ArrayList<List<Usuario>>(MAX_CONNECTIONS);
		
		if (MAX_CONNECTIONS == 1)
		{
			lists.add(list);
			return;
		}
		
		for (int i = 0; i < MAX_CONNECTIONS; i++) {
			this.lists.add(new ArrayList<Usuario>());
		}
		
		for (int j = 0; j < list.size(); j++) {
			this.lists.get(j % MAX_CONNECTIONS).add(list.get(j));
		}
	}
	
	public synchronized List<Usuario> getAvailableList()
	{
		if (lists.size() > 0)
			return this.lists.remove(0);
		
		return null;
	}
	
	public static void main(String[] args)
	{
		if (args != null && args.length == 5)
		{
			try
			{
				RMI_REGISTRY = args[0];
				MAX_CONNECTIONS = Integer.parseInt(args[1]);
				NUMBER_OF_VERTICES = Integer.parseInt(args[2]);
				VERTEX_DEGREE = Integer.parseInt(args[3]);
				TOPOLOGY = Topology.valueOf(args[4]);
			}
			catch(Exception ex)
			{
				ex.printStackTrace();
			}
			
			try {
				Naming.rebind(RMI_REGISTRY, new Server());
				System.out.println("The Server is running...");
			} catch (RemoteException e) {
				System.out.println(e.getMessage());
			} catch (MalformedURLException e) {
				System.out.println(e.getMessage());
			}
		}
		else
			System.out.println("Server: Invalid parameters input");
	}
}
